package driver

import (
	"context"
	"errors"
	"fmt"
	"log/slog"
	"math"
	"net"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"gitee.com/gousing/cache/internal"
	"gitee.com/gousing/cache/storage"
	"gitee.com/gousing/values"
	"github.com/redis/go-redis/v9"
	"github.com/vmihailenco/msgpack/v5"
	"golang.org/x/sync/singleflight"
)

// RedisOptions Redis缓存配置信息
type RedisOptions struct {
	// Client Redis服务器链接选项，使用 UniversalClient 统一客户端，UniversalClient是对 Client 、 ClusterClient 、 FailoverClient 客户端的包装。
	// 	- 根据不同的选项，客户端的类型如下：
	// 	- 如果指定了 MasterName 选项，则返回 FailoverClient 哨兵客户端。
	// 	- 如果 Addrs 是 2 个以上的地址，则返回 ClusterClient 集群客户端。
	// 	- 其他情况，返回 Client 单节点客户端。
	//  - github.com/redis/go-redis/v9
	// 	- https://redis.uptrace.dev/zh/guide/universal.html
	// 	- RedisUniversalOptions is an alias for [redis.UniversalOptions]
	Client RedisUniversalOptions
	// DefaultExpire 默认过期时间 0, 最小过期时间为1s time.Second
	// 过期时间是一个近似值，实际过期时间会在 (X-1, X] 秒之间，其中 X 是你设置的过期时间。
	DefaultExpire time.Duration
	MaxEntrySize  int64 // 单个缓存项最大数据大小，单位为MB, 默认为 32 MB
}

// Client Redis服务器链接选项，使用 UniversalClient 统一客户端，UniversalClient是对 Client 、 ClusterClient 、 FailoverClient 客户端的包装。
type RedisUniversalOptions = redis.UniversalOptions

// RedisCache Redis缓存驱动实现
type RedisCache struct {
	// Redis 客户端实例
	client redis.UniversalClient
	// Redis 客户端链接配置选项
	//  - 注意: 每一次调用 NewUniversalClient 都需要重新生成一个新的选项副本, 否则会导致配置项结构体被共享，从而导致重新初始化的 Client 实例异常
	clientOpts redis.UniversalOptions
	// Redis Server 版本号拆分后的INT切片
	verVals       []int
	ctx           context.Context       // 上下文环境, 用于控制客户端的生命周期和取消操作
	defaultExpire time.Duration         // 默认过期时间 默认: 0 表示长久存储, 注意：最小过期时间为1s
	tagKeys       *redisTags            // Redis Tag=>Keys 键名前缀映射表, 用于存储和检索与标签相关的缓存项的键名信息
	keyTags       *redisTags            // Redis Key=>Tags 键名前缀映射表, 用于存储和检索与缓存项的键名信息
	keyKinds      *redisKeyKinds        // Redis KeyKinds 键名数据类型映射表, 用于存储和检索与缓存项的Kind信息
	bytesBuffer   *internal.BytesBuffer // 字节数组对象池，用于减少内存分配和回收的开销
	maxEntrySize  int64                 // 单个缓存项最大数据大小，单位为字节, 默认为 32 MB, 最小为1MB，最大为 512MB
	loadGroup     singleflight.Group    // loadDo 单飞 singleflight 实例，用于并发控制缓存项的加载和更新等操作
	selfLocks     []sync.Mutex          // 单实例锁, 限制单个实例(主线程)应用内的并发执行, 初始化256个Key hash分片锁
	selfLocked    bool                  // 单实例锁是否启用(默认启用), 防止单个实例并发执行, 进行单元测试Redis分布锁时需要关闭
	redisLock     *redisLock            // Reds 分布式锁实现
	actionKeyPre  string                // 内部特殊动作键名前缀, 用于 RedisTag 等操作
	lockKeyPre    string                // 分布式锁键名前缀, 用于 RedisLock 等操作
	isClosed      atomic.Bool           // Redis Client 是否已关闭
	isGlobal      bool                  // Redis 驱动是否为全局默认 cache
}

func parseRedisOptions(options *RedisOptions) RedisOptions {
	if options == nil {
		return RedisOptions{
			Client:        RedisUniversalOptions{Addrs: []string{"localhost:6379"}, DB: 0, MaxIdleConns: 0, MaxActiveConns: 0},
			DefaultExpire: 0,
			MaxEntrySize:  32 * 1024 * 1024,
		}
	}
	ops := *options
	// 检查并设置客户端地址
	if len(ops.Client.Addrs) < 1 {
		ops.Client.Addrs = []string{"localhost:6379"}
	}
	if ops.Client.DB < 0 {
		ops.Client.DB = 0
	} else if ops.Client.DB > 15 {
		ops.Client.DB = 15
	}
	if ops.Client.MaxIdleConns < 0 {
		ops.Client.MaxIdleConns = 0
	}
	if ops.Client.MaxActiveConns < 0 {
		ops.Client.MaxActiveConns = 0
	} else if ops.Client.MaxActiveConns != 0 && ops.Client.MaxActiveConns < ops.Client.MaxIdleConns {
		ops.Client.MaxActiveConns = int(math.Ceil(1.25 * float64(ops.Client.MaxIdleConns)))
	}
	if ops.DefaultExpire <= 0 {
		ops.DefaultExpire = 0
	} else if ops.DefaultExpire < time.Second {
		ops.DefaultExpire = time.Second
	}
	if ops.MaxEntrySize == 0 {
		ops.MaxEntrySize = 32 // 默认为 32 MB
	} else {
		if ops.MaxEntrySize < 2 { // 单个缓存项最小数据大小为 1MB
			ops.MaxEntrySize = 1
		} else if ops.MaxEntrySize > 512 { // 单个缓存项最大数据大小为 512MB
			ops.MaxEntrySize = 512
		}
	}
	return ops
}

// NewRedis 创建一个 RedisCache 缓存驱动实例
//   - options RedisOptions
func NewRedis(options *RedisOptions) (*RedisCache, error) {
	ops := parseRedisOptions(options)
	// 注意: 每一次调用 NewUniversalClient 都需要使用选项的副本
	// 否则会导致配置项结构体被共享，从而导致重新初始化的 Client 实例异常
	clientOpts := ops.Client
	client := redis.NewUniversalClient(&clientOpts)
	// 验证是否连接到redis服务端
	ctx := context.Background()
	if err := client.Ping(ctx).Err(); err != nil {
		internal.GetLogger().Error("cache: NewRedis Client Connect Error",
			slog.String("Addrs", strings.Join(ops.Client.Addrs, ",")),
			slog.Int("DB", ops.Client.DB),
			slog.String("Error", err.Error()))
		return nil, fmt.Errorf("cache: NewRedis Client Connect Error: %s", err.Error())
	}
	internal.GetLogger().Info("cache: NewRedis Client Connect Successful",
		slog.String("Addrs", strings.Join(ops.Client.Addrs, ",")),
		slog.Int("DB", ops.Client.DB))
	rc := &RedisCache{
		client:        client,
		clientOpts:    ops.Client,
		ctx:           ctx,
		defaultExpire: ops.DefaultExpire,
		maxEntrySize:  ops.MaxEntrySize * 1024 * 1024,
		selfLocked:    true,
		actionKeyPre:  "rc:",
		lockKeyPre:    "rc:lockSync:",
		isGlobal:      false,
	}
	rc.isClosed.Store(false)
	rc.bytesBuffer = internal.NewBytesBuffer(4096)
	// 初始化标签键管理器
	rc.tagKeys = newRedisTags(rc.client, rc.actionKeyPre+"tagKeys:")
	rc.keyTags = newRedisTags(rc.client, rc.actionKeyPre+"keyTags:")
	rc.keyKinds = newRedisKeyKinds(rc.client, rc.actionKeyPre+"keyKinds:", 256)
	// 初始化单个实例锁分片
	rc.selfLocks = make([]sync.Mutex, 256)
	for i := range 256 {
		rc.selfLocks[i] = sync.Mutex{}
	}

	// 初始化Redis版本号信息
	// 获取redis版本号
	rc.verVals = make([]int, 0, 3)
	verStr, err := rc.RedisInfo("server", "redis_version")
	if err != nil {
		internal.GetLogger().Error("redisInfo get server.version", slog.String("error", err.Error()))
	} else {
		for v := range strings.SplitSeq(verStr, ".") {
			if ver, err := strconv.Atoi(v); err == nil {
				rc.verVals = append(rc.verVals, ver)
			}
		}
	}
	rc.redisLock = newRedisLock(rc.client, rc.lockKeyPre)
	return rc, nil
}

// NewRedisDefault 使用默认配置信息创建一个 RedisCache 缓存驱动实例
//   - Addrs:         []string{"localhost:6379"} 无密码连接
func NewRedisDefault() (*RedisCache, error) {
	return NewRedis(nil)
}

var _ storage.Storager = (*RedisCache)(nil)

func (c *RedisCache) getEntryOption(opts ...storage.EntryOptions) *storage.EntryOption {
	op := storage.GetEntryOptionsFromPool()
	op.Expire = c.defaultExpire
	// 追加 opts 配置信息, 并覆盖之前的配置信息
	for _, fn := range opts {
		fn(op)
	}
	return op
}

// getEntryOptionWithExpire 根据缓存项前置 originalTTL (原缓存过期时间) 获取 EntryOption 配置信息, 并以覆盖方式追加 opts 配置
func (c *RedisCache) getEntryOptionWithTTL(originalTTL time.Duration, opts ...storage.EntryOptions) *storage.EntryOption {
	op := storage.GetEntryOptionsFromPool()
	op.Expire = c.defaultExpire
	// 保持过期时间不变
	if originalTTL > 0 {
		op.Expire = originalTTL
		// 如果过期时间小于1秒, 则设置为 1s
		if op.Expire < time.Second {
			op.Expire = time.Second
		}
	} else if originalTTL == 0 {
		op.Expire = 0
	}
	// 追加 opts 配置信息, 并覆盖之前的配置信息
	for _, fn := range opts {
		fn(op)
	}
	return op
}

// getEntryOptionWithTimeout 根据缓存前置 defaultTimeout 操作耗时获取 EntryOption 配置信息, 并以覆盖方式追加 opts 配置
//   - defaultTimeout 最少耗时为 1 秒, 如果小于 1 秒则设置为 1s
func (c *RedisCache) getEntryOptionWithTimeout(defaultTimeout time.Duration, opts ...storage.EntryOptions) *storage.EntryOption {
	op := storage.GetEntryOptionsFromPool()
	op.Expire = c.defaultExpire
	op.Timeout = max(defaultTimeout, time.Second)
	// 追加 opts 配置信息, 并覆盖之前的配置信息
	for _, fn := range opts {
		fn(op)
	}
	return op
}

// RedisInfo 获取当前链接的 Redis 服务器信息
//   - section: 要获取信息组名称(不区分大小写)，例如 "server" 或 "clients"
//   - sub: 要获取信息子项(不区分大小写)，例如 "redis_version" 或 "connected_clients"
//   - RedisInfo("server", "redis_version")
//   - RedisInfo("memory", "used_memory")
//   - https://redis.ac.cn/docs/latest/commands/info/
func (c *RedisCache) RedisInfo(section string, sub string) (string, error) {
	infoStr, err := c.client.Info(c.ctx, section).Result()
	if err != nil {
		return "", err
	}
	if infoStr == "" || section == "" || sub == "" {
		return infoStr, nil
	}
	sub = strings.ToLower(sub)
	for line := range strings.SplitSeq(infoStr, "\n") {
		line = strings.TrimSpace(line)
		if line != "" && !strings.HasPrefix(line, "#") {
			parts := strings.SplitN(line, ":", 2)
			if len(parts) == 2 && sub == strings.ToLower(parts[0]) {
				return strings.TrimSpace(parts[1]), nil
			}
		}
	}
	return "", errors.New("RedisInfo not found section: " + section + ", sub:" + sub)
}

// RedisVerLessEqual 检查指定版本号是否小于等于当前客户端链接的服务器Redis版本
//   - (6) 检查当前版本是否小于等于 "6.X.X"
//   - (6,2) 检查当前版本是否小于等于 "6.2.X"
//   - (6,2,14) 检查当前版本是否小于等于 "6.2.14"
func (c *RedisCache) RedisVerLessEqual(verArgs ...int) bool {
	if len(c.verVals) == 0 || len(verArgs) == 0 {
		return false
	}
	for i, ver := range verArgs {
		if i == len(c.verVals) {
			// 待检查版本号比服务器版本号长, 例如：
			// verArgs: 6.2.14 <=> Server: 6.2
			// verArgs: 6.2 <=> Server: 6
			// 通常不会出现这种情况，除非是故意为之
			return false
		}
		if c.verVals[i] < ver {
			return true
		}
		if c.verVals[i] > ver {
			return false
		}
		// 版本号相等
		// 是否是待检查的最后一个版本号
		// 待检查的版本号比服务器版本号短或长度相等, 例如：
		// verArgs: 6.2 <=> Server: 6.2.14
		// verArgs: 6 <=> Server: 6.2.14
		if i == len(verArgs)-1 {
			for i := i + 1; i < len(c.verVals); i++ {
				if c.verVals[i] != 0 {
					return false
				}
			}
			return true
		}
		// 版本号相等且不是待检查的最后一个版本号
		// 继续比较下一个
	}
	return false
}

// RedisVerGreaterEqual 检查指定版本号是否大于等于当前客户端链接的服务器Redis版本
//   - (6) 检查当前版本是否大于等于 "6.X.X"
//   - (6,2) 检查当前版本是否大于等于 "6.2.X"
//   - (6,2,14) 检查当前版本是否大于等于 "6.2.14"
func (c *RedisCache) RedisVerGreaterEqual(verArgs ...int) bool {
	if len(c.verVals) == 0 || len(verArgs) == 0 {
		return false
	}
	for i, ver := range verArgs {
		if i == len(c.verVals) {
			// 待检查版本号比服务器版本号长, 例如：
			// verArgs: 6.2.14 <=> Server: 6.2
			// verArgs: 6.2 <=> Server: 6
			// 通常不会出现这种情况，除非是故意为之
			return false
		}
		if c.verVals[i] > ver {
			return true
		}
		if c.verVals[i] < ver {
			return false
		}
		// 版本号相等
		// 是否是待检查的最后一个版本号
		// 待检查的版本号比服务器版本号短或长度相等, 例如：
		// verArgs: 6.2 <=> Server: 6.2.14
		// verArgs: 6 <=> Server: 6.2.14
		if i == len(verArgs)-1 {
			return true
		}
		// 版本号相等且不是待检查的最后一个版本号
		// 继续比较下一个
	}
	return false
}

func (c *RedisCache) getSelfLock(key string) *sync.Mutex {
	// 256-1 = 255 余数范围为 [0, 255]
	return &c.selfLocks[internal.HashFnv32(key)&255]
}

// Scan 获取键对应的值, 并解码到 refVal
//   - refVal 是一个指向具体数据类型的指针, 用于存放反序列化后的数据
//   - 不存在或已经过期或反序列化失败则返回错误
//   - 标量类型直接解析到 refVal
//   - 其他返回数据为 msgpack 自动反序列化后解析到 refVal
func (c *RedisCache) Scan(key string, refVal any) (err error) {
	if key == "" {
		return storage.ErrKeyIsEmpty
	}
	if refVal == nil {
		return storage.ErrRefValueIsNil
	}
	return c._scan(key, refVal)
}
func (c *RedisCache) _scan(key string, refVal any) (err error) {
	kind, err := c.keyKinds.Get(key)
	if err != nil {
		if err == redis.Nil {
			return storage.ErrKeyNotExists
		}
		return err
	}
	if kind == storage.KindInvalid {
		return storage.ErrKeyKindInvalid
	}
	// storage.KindSerializer 或 storage.KindBytes 直接反序列化
	if kind == storage.KindSerializer || kind == storage.KindBytes {
		data, err := c.client.Get(c.ctx, key).Bytes()
		if err != nil {
			if err == redis.Nil {
				c.keyKinds.Delete(key)
				return storage.ErrKeyNotExists
			}
			return err
		}
		switch refVal := refVal.(type) {
		case *any:
			if refVal == nil {
				return storage.ErrRefValueIsNil
			}
			*refVal, err = c._getWithKind(kind, key)
			return err
		case **any:
			if refVal == nil || *refVal == nil {
				return storage.ErrRefValueIsNil
			}
			**refVal, err = c._getWithKind(kind, key)
			return err
		case *[]byte:
			if refVal == nil {
				return storage.ErrRefValueIsNil
			}
			*refVal = data
			return nil
		case **[]byte:
			if refVal == nil || *refVal == nil {
				return storage.ErrRefValueIsNil
			}
			**refVal = data
			return nil
		// 反序列化
		default:
			return msgpack.Unmarshal(data, refVal)
		}
	}

	// 集合类型
	if kind == storage.KindSetString || kind == storage.KindSetInt {
		err := c.client.SMembers(c.ctx, key).ScanSlice(refVal)
		if err != nil {
			if err == redis.Nil {
				return storage.ErrKeyNotExists
			}
			return err
		}
		return nil
	}
	// 其他基本类型全部存储为了String
	val, err := c.client.Get(c.ctx, key).Result()
	if err != nil {
		if err == redis.Nil {
			c.keyKinds.Delete(key)
			return storage.ErrKeyNotExists
		}
		return err
	}
	// 直接转换字符串为目标类型
	switch refVal := refVal.(type) {
	case *any:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = c._getWithKind(kind, key)
		return err
	case **any:
		if *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = c._getWithKind(kind, key)
		return err
	case *string:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToString(storage.KindString, val)
		return
	case **string:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToString(storage.KindString, val)
		return
	case *bool:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToBool(storage.KindString, val)
		return
	case **bool:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToBool(storage.KindString, val)
		return
	case *int64:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToInt64(storage.KindString, val)
		return
	case **int64:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToInt64(storage.KindString, val)
		return
	case *int32:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToInt32(storage.KindString, val)
		return
	case **int32:
		if *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToInt32(storage.KindString, val)
		return
	case *int16:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToInt16(storage.KindString, val)
		return
	case **int16:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToInt16(storage.KindString, val)
		return
	case *int8:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToInt8(storage.KindString, val)
		return
	case **int8:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToInt8(storage.KindString, val)
		return
	case *int:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToInt(storage.KindString, val)
		return
	case **int:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToInt(storage.KindString, val)
		return
	case *uint64:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToUint64(storage.KindString, val)
		return
	case **uint64:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToUint64(storage.KindString, val)
		return
	case *uint32:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToUint32(storage.KindString, val)
		return
	case **uint32:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToUint32(storage.KindString, val)
		return
	case *uint16:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToUint16(storage.KindString, val)
		return
	case **uint16:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToUint16(storage.KindString, val)
		return
	case *uint8:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToUint8(storage.KindString, val)
		return
	case **uint8:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToUint8(storage.KindString, val)
		return
	case *uint:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToUint(storage.KindString, val)
		return
	case **uint:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToUint(storage.KindString, val)
		return

	case *float64:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToFloat64(storage.KindString, val)
		return
	case **float64:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToFloat64(storage.KindString, val)
		return
	case *float32:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToFloat32(storage.KindString, val)
		return
	case **float32:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToFloat32(storage.KindString, val)
		return
	case *time.Time:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToTime(storage.KindString, val)
		return
	case **time.Time:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToTime(storage.KindString, val)
		return
	case *time.Duration:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToDuration(storage.KindString, val)
		return
	case **time.Duration:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToDuration(storage.KindString, val)
		return
	case *time.Month:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToMonth(storage.KindString, val)
		return
	case **time.Month:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToMonth(storage.KindString, val)
		return
	case *time.Weekday:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToWeekday(storage.KindString, val)
		return
	case **time.Weekday:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToWeekday(storage.KindString, val)
		return

	case *complex128:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToComplex128(storage.KindString, val)
		return
	case **complex128:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToComplex128(storage.KindString, val)
		return
	case *complex64:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToComplex64(storage.KindString, val)
		return
	case **complex64:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToComplex64(storage.KindString, val)
		return
	case *net.IP:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToIP(storage.KindString, val)
		return
	case **net.IP:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToIP(storage.KindString, val)
		return
	case *[]byte:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToBytes(storage.KindString, val)
		return
	case **[]byte:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToBytes(storage.KindString, val)
		return

		// SetString 集合类型转换
	case *map[string]struct{}:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToSetStringMap(storage.KindString, val)
		return
	case **map[string]struct{}:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToSetStringMap(storage.KindString, val)
		return
		// SetInteger 集合类型转换
	case *map[int64]struct{}:
		if refVal == nil {
			return storage.ErrRefValueIsNil
		}
		*refVal, err = storage.ValueToSetIntMap(storage.KindString, val)
		return
	case **map[int64]struct{}:
		if refVal == nil || *refVal == nil {
			return storage.ErrRefValueIsNil
		}
		**refVal, err = storage.ValueToSetIntMap(storage.KindString, val)
		return
	}
	return storage.ErrKeyKindInvalid
}

// Get 获取键对应的值
//   - 不存在或如果已经过期则返回错误
//   - 存在则返回缓存值:
//   - - 标量类型直接返回 Any 缓存数据值
//   - - 其他返回数据为 msgpack 自动反序列化后的 Any 缓存数据值
func (c *RedisCache) Get(key string) (any, error) {
	if key == "" {
		return nil, storage.ErrKeyIsEmpty
	}
	kind, err := c.keyKinds.Get(key)
	if err != nil {
		if err == redis.Nil {
			c.keyKinds.Delete(key)
			return nil, storage.ErrKeyNotExists
		}
		return nil, err
	}
	return c._getWithKind(kind, key)
}
func (c *RedisCache) _getWithKind(kind storage.Kind, key string) (any, error) {
	if kind == storage.KindInvalid {
		return nil, storage.ErrKeyKindInvalid
	}
	// 序列化 或 字节切片 类型
	if kind == storage.KindSerializer || kind == storage.KindBytes {
		data, err := c.client.Get(c.ctx, key).Bytes()
		if err != nil {
			if err == redis.Nil {
				c.keyKinds.Delete(key)
				return nil, storage.ErrKeyNotExists
			}
			return nil, err
		}
		if kind == storage.KindBytes {
			return data, nil
		}
		// storage.KindSerializer 反序列化
		var val any
		err = msgpack.Unmarshal(data, &val)
		return val, err
	}
	// 集合类型
	if kind == storage.KindSetString || kind == storage.KindSetInt {
		data, err := c.client.SMembers(c.ctx, key).Result()
		if err != nil {
			if err == redis.Nil {
				c.keyKinds.Delete(key)
				return nil, storage.ErrKeyNotExists
			}
			return nil, err
		}
		if kind == storage.KindSetString {
			return data, nil
		}
		return storage.ValueToSetIntSlice(storage.KindSetInt, data)
	}

	// 其他基本类型全部存储为了String
	val, err := c.client.Get(c.ctx, key).Result()
	if err != nil {
		if err == redis.Nil {
			c.keyKinds.Delete(key)
			return nil, storage.ErrKeyNotExists
		}
		return nil, err
	}
	switch kind {
	case storage.KindNil:
		return nil, nil
	case storage.KindString:
		return val, nil
	case storage.KindBool:
		return storage.ValueToBool(storage.KindString, val)
	case storage.KindInt64:
		return storage.ValueToInt64(storage.KindString, val)
	case storage.KindInt32:
		return storage.ValueToInt32(storage.KindString, val)
	case storage.KindInt16:
		return storage.ValueToInt16(storage.KindString, val)
	case storage.KindInt8:
		return storage.ValueToInt8(storage.KindString, val)
	case storage.KindInt:
		return storage.ValueToInt(storage.KindString, val)
	case storage.KindUint64:
		return storage.ValueToUint64(storage.KindString, val)
	case storage.KindUint32:
		return storage.ValueToUint32(storage.KindString, val)
	case storage.KindUint16:
		return storage.ValueToUint16(storage.KindString, val)
	case storage.KindUint8:
		return storage.ValueToUint8(storage.KindString, val)
	case storage.KindUint:
		return storage.ValueToUint(storage.KindString, val)

	case storage.KindFloat64:
		return storage.ValueToFloat64(storage.KindString, val)
	case storage.KindFloat32:
		return storage.ValueToFloat32(storage.KindString, val)

	case storage.KindComplex128:
		return storage.ValueToComplex128(storage.KindString, val)
	case storage.KindComplex64:
		return storage.ValueToComplex64(storage.KindString, val)

	case storage.KindTime:
		return storage.ValueToTime(storage.KindString, val)
	case storage.KindDuration:
		return storage.ValueToDuration(storage.KindString, val)
	case storage.KindWeekday:
		return storage.ValueToWeekday(storage.KindString, val)
	case storage.KindMonth:
		return storage.ValueToMonth(storage.KindString, val)

	case storage.KindIP:
		return storage.ValueToIP(storage.KindString, val)

	default:
		return nil, storage.ErrKeyKindInvalid
	}

}

// GetEntry 获取键对应的缓存项副本信息, 如果不存在或已经过期则返回错误
func (c *RedisCache) GetEntry(key string) (entry *storage.Entry, err error) {
	if key == "" {
		return nil, storage.ErrKeyIsEmpty
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	kind, err := c.keyKinds.Get(key)
	if err != nil {
		if err == redis.Nil {
			c.keyKinds.Delete(key)
			return nil, storage.ErrKeyNotExists
		}
		return nil, err
	}
	anyVal, err := c._getWithKind(kind, key)
	if err != nil {
		if err == redis.Nil {
			c.keyKinds.Delete(key)
			return nil, storage.ErrKeyNotExists
		}
		return nil, err
	}
	ttl := c.TTL(key)
	if ttl == -1 {
		return nil, storage.ErrKeyExpired
	}
	return &storage.Entry{
		Key:    key,
		Kind:   kind,
		Value:  anyVal,
		Size:   storage.EntryValueSize(kind, key, anyVal),
		Expire: ttl,
		Tags:   c.GetKeyTags(key),
	}, nil
}

// GetAndSet 获取键对应的旧值并设置新值
//   - 不存在或如果已经过期则返回nil
//   - 存在则返回旧值:
//   - - 标量类型直接返回 Any 缓存数据值
//   - - 其他返回数据为 msgpack 自动反序列化后的 Any 缓存数据值
func (c *RedisCache) GetAndSet(key string, value any, opts ...storage.EntryOptions) (any, error) {
	if key == "" {
		return nil, storage.ErrKeyIsEmpty
	}
	if value == nil {
		return nil, storage.ErrValueIsNil
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	// 自 Redis 6.2.0 版本起，GETSET 此命令被视为已弃用
	// 通过 Get + Set 实现
	val, _ := c.Get(key)
	err := c.Set(key, value, opts...)
	return val, err
}

// GetOrSet 获取键对应的值, 如不存在则设置为指定的值
//   - 不存在或如果已经过期则进行设置并返回设置的值
//   - 存在则返回旧值:
//   - - 标量类型直接返回 Any 缓存数据值
//   - - 其他返回数据为 msgpack 自动反序列化后的 Any 缓存数据值
func (c *RedisCache) GetOrSet(key string, value any, opts ...storage.EntryOptions) (any, error) {
	if key == "" {
		return nil, storage.ErrKeyIsEmpty
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	val, err := c.Get(key)
	if err == nil {
		return val, nil
	}
	if value == nil {
		return nil, storage.ErrValueIsNil
	}
	err = c.Set(key, value, opts...)
	return value, err
}

// GetAndDelete 获取键对应的值, 并删除该键
//   - 不存在或如果已经过期则返回错误
//   - 存在则返回值:
//   - - 标量类型直接返回 Any 缓存数据值
//   - - 其他返回数据为 msgpack 自动反序列化后的 Any 缓存数据值
func (c *RedisCache) GetAndDelete(key string) (any, error) {
	if key == "" {
		return nil, storage.ErrKeyIsEmpty
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	val, err := c.Get(key)
	c.Delete(key)
	if err != nil {
		return nil, err
	}
	return val, nil
}

// GetTags 获取所有Tag标签
func (c *RedisCache) GetTags() []string {
	return c.tagKeys.GetKeys()
}

// GetTagKeys 获取指定 Tag 标签对应的所有缓存 Keys 键名
func (c *RedisCache) GetTagKeys(tag string) []string {
	if tag == "" {
		return []string{}
	}
	return c.tagKeys.GetItems(tag)
}

// GetKeyTags 获取指定键名 Key 对应的所有缓存 Tags 标签
func (c *RedisCache) GetKeyTags(key string) []string {
	if key == "" {
		return []string{}
	}
	return c.keyTags.GetItems(key)
}

type redisSetMode int8

const (
	redisSetDefault redisSetMode = iota // 覆盖模式, 默认
	redisSetNX                          // 仅当键不存在时才设置值, 并返回设置的错误信息
	redisSetXX                          // 仅当键存在时才设置值, 并返回设置的错误信息
)

// Set 设置键对应的值, 可以通过 EntryOption 设置 Expire, Tag 等选项
//   - key 为空 或 value 为nil 则返回错误
//   - 如缓存 key 已存在且 opts 为空, 则保持过期时间Tags等选项
//   - 如缓存 key 已存在且 opts 不为空, 则更新过期时间Tags等opts选项(原Tag保留, 如有新Tag则追加)
//   - 如缓存 key 不存在且 opts 为空, 则使用默认选项存储数据
//   - 如缓存 key 不存在且 opts 不为空, 则使用opts选项存储数据
func (c *RedisCache) Set(key string, value any, opts ...storage.EntryOptions) error {
	if key == "" {
		return storage.ErrKeyIsEmpty
	}
	if value == nil {
		return storage.ErrValueIsNil
	}
	_, err := c._setAny(redisSetDefault, key, value, opts...)
	return err
}

// _setAny 设置键对应的值, 可以通过 EntryOption 设置 Expire, Tag 等选项
//   - key 为空 或 value 为nil 则返回错误
//   - 如缓存 key 已存在且 opts 为空, 则保持过期时间Tags等选项
//   - 如缓存 key 已存在且 opts 不为空, 则更新过期时间Tags等opts选项(原Tag保留, 如有新Tag则追加)
//   - 如缓存 key 不存在且 opts 为空, 则使用默认选项存储数据
//   - 如缓存 key 不存在且 opts 不为空, 则使用opts选项存储数据
func (c *RedisCache) _setAny(mode redisSetMode, key string, value any, opts ...storage.EntryOptions) (succeed bool, err error) {
	if key == "" {
		return false, storage.ErrKeyIsEmpty
	}
	if value == nil {
		return false, storage.ErrValueIsNil
	}

	kind := storage.KindDetect(value)
	if kind == storage.KindInvalid {
		return false, storage.ErrKeyKindInvalid
	}
	// 序列化缓存数据，序列化后直接存储
	if kind == storage.KindSerializer {
		bytesBuf := c.bytesBuffer.Get()
		defer c.bytesBuffer.Put(bytesBuf)
		enc := msgpack.GetEncoder()
		enc.Reset(bytesBuf)
		if err = enc.Encode(value); err != nil {
			msgpack.PutEncoder(enc)
			return false, storage.ErrValueMarshal
		}
		msgpack.PutEncoder(enc)
		return c._setBytes(mode, key, bytesBuf.Bytes(), kind, opts...)
	}
	// 字节缓存数据，直接存储
	if kind == storage.KindBytes {
		if data, ok := value.([]byte); ok {
			return c._setBytes(mode, key, data, kind, opts...)
		}
		return false, storage.ErrValueDataInvalid
	}

	// 其他基本类型, 转为字符串缓存数据
	value, err = storage.ValueToString(kind, value)
	if err != nil {
		return false, err
	}
	valueSize := len(value.(string))
	if int64(valueSize) > c.maxEntrySize {
		internal.GetLogger().Error("cache: value size: '" + internal.FormatSize(int64(valueSize)) + "' is too large of key: '" + key + "', entry max size: '" + internal.FormatSize(c.maxEntrySize) + "'")
		return false, storage.ErrValueTooLarge
	}
	var op *storage.EntryOption
	if c.client.Exists(c.ctx, key).Val() == 0 {
		op = c.getEntryOption(opts...)
	} else {
		op = c.getEntryOptionWithTTL(c.TTL(key), opts...)
	}
	defer op.Release()

	var (
		status string
	)
	switch mode {
	case redisSetDefault:
		status, err = c.client.Set(c.ctx, key, value, op.Expire).Result()
		succeed = status == "OK"
	case redisSetNX:
		succeed, err = c.client.SetNX(c.ctx, key, value, op.Expire).Result()
	case redisSetXX:
		succeed, err = c.client.SetXX(c.ctx, key, value, op.Expire).Result()
	default:
		status, err = c.client.Set(c.ctx, key, value, op.Expire).Result()
		succeed = status == "OK"
	}
	if !succeed || err != nil {
		if err != nil {
			return false, err
		}
		return false, errors.New("cache: set key '" + key + "' failed")
	}
	// 设置缓存类型到 keyKinds 集合中, 用于后续快速查询缓存类型
	if err := c.keyKinds.Set(key, kind, op.Expire); err != nil {
		return false, err
	}
	if len(op.Tags) > 0 {
		for _, tag := range op.Tags {
			c.tagKeys.AddKV(tag, key)
			c.keyTags.AddKV(key, tag)
		}
	}
	return true, nil
}

// _setBytes 设置键对应的值, 可以通过 EntryOption 设置 Expire, Tag 等选项
//   - key 为空 或 value 为nil 则返回错误
//   - 如缓存 key 已存在且 opts 为空, 则保持过期时间Tags等选项
//   - 如缓存 key 已存在且 opts 不为空, 则更新过期时间Tags等opts选项(原Tag保留, 如有新Tag则追加)
//   - 如缓存 key 不存在且 opts 为空, 则使用默认选项存储数据
//   - 如缓存 key 不存在且 opts 不为空, 则使用opts选项存储数据
func (c *RedisCache) _setBytes(mode redisSetMode, key string, value []byte, kind storage.Kind, opts ...storage.EntryOptions) (succeed bool, err error) {
	if key == "" {
		return false, storage.ErrKeyIsEmpty
	}
	if value == nil {
		return false, storage.ErrValueIsNil
	}
	valueSize := len(value)
	if int64(valueSize) > c.maxEntrySize {
		internal.GetLogger().Error("cache: value size: '" + internal.FormatSize(int64(valueSize)) + "' is too large of key: '" + key + "', entry max size: '" + internal.FormatSize(c.maxEntrySize) + "'")
		return false, storage.ErrValueTooLarge
	}
	var op *storage.EntryOption
	if c.client.Exists(c.ctx, key).Val() == 0 {
		op = c.getEntryOption(opts...)
	} else {
		op = c.getEntryOptionWithTTL(c.TTL(key), opts...)
	}
	defer op.Release()

	var (
		status string
	)
	switch mode {
	case redisSetDefault:
		status, err = c.client.Set(c.ctx, key, value, op.Expire).Result()
		succeed = status == "OK"
	case redisSetNX:
		succeed, err = c.client.SetNX(c.ctx, key, value, op.Expire).Result()
	case redisSetXX:
		succeed, err = c.client.SetXX(c.ctx, key, value, op.Expire).Result()
	default:
		status, err = c.client.Set(c.ctx, key, value, op.Expire).Result()
		succeed = status == "OK"
	}
	if !succeed || err != nil {
		if err != nil {
			return false, err
		}
		return false, errors.New("cache: set key '" + key + "' failed")
	}
	// 设置缓存类型到 keyKinds 集合中, 用于后续快速查询缓存类型
	if err := c.keyKinds.Set(key, kind, op.Expire); err != nil {
		return false, err
	}
	if len(op.Tags) > 0 {
		for _, tag := range op.Tags {
			c.tagKeys.AddKV(tag, key)
			c.keyTags.AddKV(key, tag)
		}
	}
	return true, nil
}

// SetWithExists 仅当缓存 Key 键存在时才更新设置值, 返回True表示更新成功
func (c *RedisCache) SetWithExists(key string, value any, opts ...storage.EntryOptions) (updated bool, err error) {
	if key == "" {
		return false, storage.ErrKeyIsEmpty
	}
	if value == nil {
		return false, storage.ErrValueIsNil
	}
	if c.client.Exists(c.ctx, key).Val() == 0 {
		return false, storage.ErrKeyNotExists
	}
	return c._setAny(redisSetXX, key, value, opts...)
}

// SetWithNotExists 仅当缓存 Key 键不存在时才设置值, 返回True表示设置成功
func (c *RedisCache) SetWithNotExists(key string, value any, opts ...storage.EntryOptions) (added bool, err error) {
	if key == "" {
		return false, storage.ErrKeyIsEmpty
	}
	if value == nil {
		return false, storage.ErrValueIsNil
	}
	if c.client.Exists(c.ctx, key).Val() == 1 {
		return false, storage.ErrKeyIsExists
	}
	return c._setAny(redisSetNX, key, value, opts...)
}

// Incr 原子性地将键对应的值增加1, 并返回新的值
//   - 如果键不存在, 则将原子值设为1 (Set Use Opts)
//   - 如果键存在, 则更新的新值(Set Without Opts)
func (c *RedisCache) Incr(key string, opts ...storage.EntryOptions) (int64, error) {
	return c.IncrBy(key, 1, opts...)
}

// IncrBy 原子性地将键对应的值增加Step步进值, 并返回新的值
//   - 如果键不存在, 则将原子值设为Step (Set Use Opts)
//   - 如果键存在, 则更新的新值(Set Without Opts)
func (c *RedisCache) IncrBy(key string, step int64, opts ...storage.EntryOptions) (int64, error) {
	// 这里未使用 Redis IncrBy 命令, 因为缓存统一对Value进行了序列化处理
	// 这里使用 Update 逻辑完成原子性操作
	if key == "" {
		return step, storage.ErrKeyIsEmpty
	}
	// 单个实例锁, 限制单个实例(主线程)应用内的并发执行
	if c.selfLocked {
		sLock := c.getSelfLock(key)
		sLock.Lock()
		defer sLock.Unlock()
	}

	// 通过 RedisLock 分布式锁限制其他实例线程(客户端)并发执行同一键的重复查询
	var rLock *redisLockEntry
	if len(opts) == 0 {
		rLock = c.redisLock.NewDefault(key)
	} else {
		op := c.getEntryOptionWithTimeout(c.redisLock.DefaultTimeout(), opts...)
		defer op.Release()
		rLock = c.redisLock.NewTimeout(key, op.Timeout)
	}

	if err := rLock.TryLock(); err != nil {
		return step, err
	}
	defer func() {
		if err := rLock.UnlockAndPutPool(); err != nil {
			internal.GetLogger().Error("cache: DecrBy Unlock error", slog.String("key", key), slog.Any("err", err))
		}
	}()
	// 缓存类型校验, 仅支持 Int/Uint 系列类型
	kind, err := c.keyKinds.Get(key)
	if err != nil {
		if err == storage.ErrKeyNotExists {
			// 不存在, 存储后返回
			_, err = c._setAny(redisSetDefault, key, step, opts...)
		}
		return step, err
	}
	switch kind {
	case storage.KindInt64:
		// 缓存存在且是 Int64, IncrBy 更新后返回
		return c.client.IncrBy(c.ctx, key, step).Result()
	case storage.KindInt32, storage.KindInt16, storage.KindInt8, storage.KindInt,
		storage.KindUint64, storage.KindUint32, storage.KindUint16, storage.KindUint8, storage.KindUint:
		var val int64
		// 缓存存在, 但不是 Int64, 尝试转为 Int64 更新后返回
		if err := c.Scan(key, &val); err == nil {
			val = val + step
			_, err := c._setAny(redisSetDefault, key, val, opts...)
			return val, err
		} else {
			// 重复校验, 被其他实例删除或过期
			if err == storage.ErrKeyNotExists {
				_, err = c._setAny(redisSetDefault, key, step, opts...)
			}
			return step, err
		}
	default:
		return step, storage.ErrValueIsNotInteger
	}
}

// Decr 原子性地将键对应的值减少1, 并返回新的值
//   - 如果键不存在, 则将原子值设为 -1 (Set Use Opts)
//   - 如果键存在, 则更新的新值(Set Without Opts)
func (c *RedisCache) Decr(key string, opts ...storage.EntryOptions) (int64, error) {
	return c.DecrBy(key, 1, opts...)
}

// DecrBy 原子性地将键对应的值减少Step步进值, 并返回新的值
//   - 如果键不存在, 则将原子值设为 -step (Set Use Opts)
//   - 如果键存在, 则更新的新值(Set Without Opts)
func (c *RedisCache) DecrBy(key string, step int64, opts ...storage.EntryOptions) (int64, error) {
	// 这里未使用 Redis DecrBy 命令, 因为缓存统一对Value进行了序列化处理
	// 这里使用 Update 逻辑完成原子性操作
	if key == "" {
		return 0, storage.ErrKeyIsEmpty
	}
	// 单个实例锁, 限制单个实例(主线程)应用内的并发执行
	if c.selfLocked {
		sLock := c.getSelfLock(key)
		sLock.Lock()
		defer sLock.Unlock()
	}

	// 通过 RedisLock 分布式锁限制其他实例线程(客户端)并发执行同一键的重复查询
	var rLock *redisLockEntry
	if len(opts) == 0 {
		rLock = c.redisLock.NewDefault(key)
	} else {
		op := c.getEntryOptionWithTimeout(c.redisLock.DefaultTimeout(), opts...)
		defer op.Release()
		rLock = c.redisLock.NewTimeout(key, op.Timeout)
	}

	if err := rLock.TryLock(); err != nil {
		return 0, err
	}
	defer func() {
		if err := rLock.UnlockAndPutPool(); err != nil {
			internal.GetLogger().Error("cache: DecrBy Unlock error", slog.String("key", key), slog.Any("err", err))
		}
	}()

	// 缓存类型校验, 仅支持 Int/Uint 系列类型
	kind, err := c.keyKinds.Get(key)
	if err != nil {
		if err == storage.ErrKeyNotExists {
			// 不存在, 存储后返回
			_, err = c._setAny(redisSetDefault, key, -step, opts...)
		}
		return -step, err
	}
	switch kind {
	case storage.KindInt64:
		// 缓存存在且是 Int64, DecrBy 更新后返回
		return c.client.DecrBy(c.ctx, key, step).Result()
	case storage.KindInt32, storage.KindInt16, storage.KindInt8, storage.KindInt,
		storage.KindUint64, storage.KindUint32, storage.KindUint16, storage.KindUint8, storage.KindUint:
		var val int64
		// 缓存存在, 但不是 Int64, 尝试转为 Int64 更新后返回
		if err := c.Scan(key, &val); err == nil {
			val = val - step
			_, err := c._setAny(redisSetDefault, key, val, opts...)
			return val, err
		} else {
			// 重复校验, 被其他实例删除或过期
			if err == storage.ErrKeyNotExists {
				_, err = c._setAny(redisSetDefault, key, -step, opts...)
			}
			return -step, err
		}
	default:
		return -step, storage.ErrValueIsNotInteger
	}
}

// LoadDo 获取键对应的值, 并反序列化解析后赋值到 refVal
//   - refVal 是一个指向具体类型的指针, 用于存放反序列化后的数据
//   - 缓存存在: 则反序列化解析后赋值到 refVal
//   - 缓存不存在: 则执行 Fn 函数获取查询值后设置为缓存(Set Use Opts), 最后赋值到 refVal
//   - 注意: 请确保 refVal(&T) 的类型和 do() 函数的返回值T类型一致
func (c *RedisCache) LoadDo(key string, refVal any, do func() (setVal any, err error), opts ...storage.EntryOptions) error {
	if key == "" {
		return storage.ErrKeyIsEmpty
	}
	if refVal == nil {
		return storage.ErrRefValueIsNil
	}
	// 单个实例锁, 限制单个实例(主线程)应用内的并发执行
	if c.selfLocked {
		sLock := c.getSelfLock(key)
		sLock.Lock()
		defer sLock.Unlock()
	}
	// 尝试获取缓存值
	if err := c._scan(key, refVal); err == nil {
		return nil
	}
	// 通过 singleflight 限制单个实例(主线程)应用内的并发执行
	_, err, shared := c.loadGroup.Do(key, func() (any, error) {
		// 捕获异常
		defer func() {
			if r := recover(); r != nil {
				internal.GetLogger().Error("cache: LoadDo recover with panic", slog.String("key", key), slog.Any("panic", r))
			}
		}()
		// 分布式锁, 限制其他线程(客户端)并发执行同一键的重复查询
		var rLock *redisLockEntry
		if len(opts) == 0 {
			rLock = c.redisLock.NewDefault(key)
		} else {
			op := c.getEntryOptionWithTimeout(c.redisLock.DefaultTimeout(), opts...)
			defer op.Release()
			rLock = c.redisLock.NewTimeout(key, op.Timeout)
		}
		if err := rLock.TryLock(); err != nil {
			return nil, err
		}
		defer func() {
			if err := rLock.UnlockAndPutPool(); err != nil {
				internal.GetLogger().Error("cache: LoadDo Unlock error", slog.String("key", key), slog.Any("err", err))
			}
		}()
		// 双重检查缓存（避免锁内重复查询）再次尝试获取缓存值
		if err := c._scan(key, refVal); err == nil {
			return nil, nil
		}
		// 执行 do 函数获取缓存值, 并设置到缓存中
		value, err := do()
		if err != nil {
			return nil, err
		}
		if value == nil {
			return nil, storage.ErrValueIsNil
		}
		_, err = c._setAny(redisSetDefault, key, value, opts...)
		if err != nil {
			return nil, err
		}
		// 共享缓存值, 直接赋值到 refVal
		if err := c._scan(key, refVal); err != nil {
			return nil, err
		}
		return nil, nil
	})
	if err != nil {
		return err
	}
	if shared {
		// 共享缓存值, 直接赋值到 refVal
		if err := c._scan(key, refVal); err != nil {
			return err
		}
	}
	return nil
}

// UpdateFn 更新键对应的值
//   - 如果键不存在, 则执行 Fn 函数设置值并返回设置的默认值(Set Use Opts), 并返回设置的默认值
//   - 如果键存在, 则执行 Fn 函数更新值并返回更新的新值(Set Without Opts), 并返回更新的新值
func (c *RedisCache) UpdateFn(key string, fn func(getVal any, exits bool) (updateVal any, err error), opts ...storage.EntryOptions) (any, error) {
	if key == "" {
		return nil, storage.ErrKeyIsEmpty
	}
	// 单个实例锁, 限制单个实例(主线程)应用内的并发执行
	if c.selfLocked {
		sLock := c.getSelfLock(key)
		sLock.Lock()
		defer sLock.Unlock()
	}

	// 通过 RedisLock 分布式锁限制其他实例线程(客户端)并发执行同一键的重复查询
	var rLock *redisLockEntry
	if len(opts) == 0 {
		rLock = c.redisLock.NewDefault(key)
	} else {
		op := c.getEntryOptionWithTimeout(c.redisLock.DefaultTimeout(), opts...)
		defer op.Release()
		rLock = c.redisLock.NewTimeout(key, op.Timeout)
	}

	if err := rLock.TryLock(); err != nil {
		return nil, err
	}
	defer func() {
		if err := rLock.UnlockAndPutPool(); err != nil {
			internal.GetLogger().Error("cache: UpdateFn Unlock error", slog.String("key", key), slog.Any("err", err))
		}
	}()
	// 捕获异常
	defer func() {
		if r := recover(); r != nil {
			internal.GetLogger().Error("cache: UpdateFn recover with panic", slog.String("key", key), slog.Any("panic", r))
		}
	}()
	// 执行 fn 函数更新缓存值, 并设置到缓存中
	var updateVal any
	getVal, err := c.Get(key)
	exits := err == nil
	if exits {
		updateVal, err = fn(getVal, true)
	} else {
		updateVal, err = fn(nil, false)
	}
	if err != nil {
		return updateVal, err
	}
	c._setAny(redisSetDefault, key, updateVal, opts...)
	return updateVal, err
}

// Expire 设置指定缓存 Key 的存活时间(精度为秒)
func (c *RedisCache) Expire(key string, duration time.Duration) error {
	_, err := c.client.Expire(c.ctx, key, duration).Result()
	switch err {
	case nil:
		return nil
	case redis.Nil:
		return storage.ErrKeyNotExists
	}
	return err
}

// ExpireAt 设置指定缓存 Key 的存活时间(精度为秒)
func (c *RedisCache) ExpireAt(key string, expireAt time.Time) error {
	if key == "" {
		return storage.ErrKeyIsEmpty
	}
	if expireAt.Before(time.Now()) {
		return storage.ErrSetExpireAt
	}
	_, err := c.client.ExpireAt(c.ctx, key, expireAt).Result()
	switch err {
	case nil:
		return nil
	case redis.Nil:
		return storage.ErrKeyNotExists
	}
	return err
}

// TTL 获取键的剩余存活时间(精度为秒)
//   - 不存在或如果已经过期则返回-1
//   - 如果设置了永久存活则返回0
func (c *RedisCache) TTL(key string) time.Duration {
	if key == "" {
		return -1
	}
	// 在 Redis 2.6 或更早版本中，如果密钥不存在或密钥存在但没有关联的过期时间，则命令返回-1。
	// 从 Redis 2.8 开始，错误情况下的返回值发生了变化：
	// 如果密钥不存在，则命令返回-2。
	// 如果密钥存在但没有关联的过期时间，则该命令返回-1。
	v, err := c.client.TTL(c.ctx, key).Result()
	if err != nil || v == -2 {
		if err == redis.Nil {
		}
		return -1
	}
	if v == -1 {
		if c.RedisVerLessEqual(2, 6) {
			return -1
		}
		// 永久存活, 返回0秒
		return 0
	}
	return v
}

// Contains 判断缓存 Key 是否存在, 支持多个键查询, 只要有一个键不存在则返回false
func (c *RedisCache) Contains(keys ...string) bool {
	if len(keys) == 0 {
		return false
	}
	// 从 Redis 版本 3.0.3 开始：接受多个key参数
	return c.client.Exists(c.ctx, keys...).Val() == int64(len(keys))
}

// ContainsSome 判断缓存 Key 是否存在, 支持多个键查询, 如果有一个或多个键存在则返回true
func (c *RedisCache) ContainsSome(keys ...string) bool {
	if len(keys) == 0 {
		return false
	}
	return c.client.Exists(c.ctx, keys...).Val() > 0
}

// ContainsTags 检查 Tag 标签是否存在, 支持多个键查询, 只要有一个键不存在则返回false
func (c *RedisCache) ContainsTags(tags ...string) bool {
	if len(tags) == 0 {
		return false
	}
	for _, tag := range tags {
		if !c.tagKeys.Has(tag) {
			return false
		}
	}
	return true
}

// ContainsSomeTags 检查 Tag 标签是否存在, 支持多个键查询, 如果有一个或多个键存在则返回true
func (c *RedisCache) ContainsSomeTags(tags ...string) bool {
	if len(tags) == 0 {
		return false
	}
	for _, tag := range tags {
		if c.tagKeys.Has(tag) {
			return true
		}
	}
	return false
}

// ContainsTagKey 检查 Tag/Key 标签对是否存在
func (c *RedisCache) ContainsTagKey(tag, key string) bool {
	return c.tagKeys.HasKV(tag, key)
}

// Metrics 返回监控指标
func (c *RedisCache) Metrics() *storage.Metrics {
	var (
		totalSize    = c.TotalSize()
		totalCount   = c.TotalCount()
		totalHits    = int64(c.client.PoolStats().Hits)
		totalMisses  = int64(c.client.PoolStats().Misses)
		totalEvicts  int64
		totalExpires int64
	)
	// stats.evicted_keys 因 maxmemory 限制而被逐出的键数量
	if val, err := c.RedisInfo("stats", "evicted_keys"); err == nil {
		totalEvicts = values.ToInt64(val)
	}
	// stats.expired_keys 键过期事件总数
	if val, err := c.RedisInfo("stats", "expired_keys"); err == nil {
		totalExpires = values.ToInt64(val)
	}
	// 总请求数 = 命中次数 + 未命中次数
	totalRequests := totalHits + totalMisses
	var hitRate, evictionRate, missRate string
	if totalRequests > 0 {
		hitRate = fmt.Sprintf("%.2f%%", float64(totalHits)/float64(totalRequests)*100)
	} else {
		hitRate = "0.00%"
	}
	if totalEvicts > 0 {
		evictionRate = fmt.Sprintf("%.2f%%", float64(totalEvicts)/float64(totalRequests)*100)
	} else {
		evictionRate = "0.00%"
	}
	if totalMisses > 0 {
		missRate = fmt.Sprintf("%.2f%%", float64(totalMisses)/float64(totalRequests)*100)
	} else {
		missRate = "0.00%"
	}

	AverageItemSize := int64(0)
	if totalCount > 0 {
		AverageItemSize = totalSize / totalCount
	}

	var formatMaxSize string
	if val := c.MaxSize(); val > 0 {
		formatMaxSize = internal.FormatSize(val)
	} else {
		formatMaxSize = "unlimited"
	}

	return &storage.Metrics{
		MaxSize:         formatMaxSize,
		TotalSize:       internal.FormatSize(totalSize),
		TotalCount:      totalCount,
		TotalRequests:   totalRequests,
		TotalHits:       totalHits,
		TotalMisses:     totalMisses,
		TotalEvicts:     totalEvicts,
		TotalExpires:    totalExpires,
		HitRate:         hitRate,
		EvictRate:       evictionRate,
		MissRate:        missRate,
		AverageItemSize: internal.FormatSize(AverageItemSize),
	}
}

// Cleanup 手动清理过期数据
func (c *RedisCache) Cleanup() error {
	// Redis 无手动触发清理过期数据需要
	return nil
}

// Delete 删除一个或多个键
func (c *RedisCache) Delete(keys ...string) error {
	if len(keys) == 0 {
		return storage.ErrKeyIsEmpty
	}
	_, err := c.client.Del(c.ctx, keys...).Result()
	if err != nil {
		if err == redis.Nil {
			return storage.ErrKeyNotExists
		}
		return err
	}
	// 删除类型数据
	_ = c.keyKinds.Delete(keys...)
	// 删除标签关系
	for _, key := range keys {
		if tags := c.keyTags.GetItems(key); len(tags) > 0 {
			for _, tag := range tags {
				c.keyTags.RemoveKV(key, tag)
				c.tagKeys.RemoveKV(tag, key)
			}
		}
	}
	return nil
}

// DeleteTags 依据缓存Tag标签删除缓存项, 同时tag 标签以及对应的 key <=> tags 和 tag <=> keys 的索引集合数据
func (c *RedisCache) DeleteTags(tags ...string) error {
	if len(tags) == 0 {
		return nil
	}
	for _, tag := range tags {
		if keys := c.tagKeys.GetItems(tag); len(keys) > 0 {
			c.Delete(keys...)
			c.tagKeys.Remove(tag)
		}
	}
	return nil
}

// Clear 清空所有数据(同步操作)
func (c *RedisCache) Clear() error {
	_, err := c.client.FlushDB(c.ctx).Result()
	return err
}

// ClearAsync 清空所有数据(异步操作)
func (c *RedisCache) ClearAsync() error {
	//  Redis 6.2 及以上版本中可以通过该配置决定默认是同步还是异步
	if c.RedisVerGreaterEqual(6, 2) {
		_, err := c.client.FlushDBAsync(c.ctx).Result()
		return err
	}
	// Redis 6.2 以下版本不支持异步清空，只能同步操作
	_, err := c.client.FlushDB(c.ctx).Result()
	return err
}

// TotalSize 返回当前缓存的总大小(单位: byte)
func (c *RedisCache) TotalSize() int64 {
	// https://redis.net.cn/order/3676.html
	val, err := c.RedisInfo("memory", "used_memory_dataset")
	if err != nil {
		val, err = c.RedisInfo("memory", "used_memory_rss")
		if err != nil {
			val, err = c.RedisInfo("memory", "used_memory")
			if err != nil {
				return 0
			}
		}
	}
	return values.ToInt64(val)
}

// TotalCount 返回当前缓存项(Key)的总数量(单位: 个)
func (c *RedisCache) TotalCount() int64 {
	// Redis Dbsize 命令用于返回当前数据库的 key 的数量
	count, _ := c.client.DBSize(c.ctx).Result()
	// tagKeys,keyTags, lock 分布式锁特殊缓存数量不算在内
	keys, err := c.client.Keys(c.ctx, c.actionKeyPre+"*").Result()
	if err == nil {
		count -= int64(len(keys))
	}
	return count
}

// MaxSize 返回当前缓存实例的最大容量(单位: byte)
//   - Redis 如果返回值为 0, 则表示没有限制
func (c *RedisCache) MaxSize() int64 {
	// https://redis.net.cn/order/3676.html
	val, err := c.RedisInfo("memory", "maxmemory")
	if err != nil {
		return 0
	}
	return values.ToInt64(val)
}

// MaxEntrySize 获取单个缓存项数据限制的大小(单位: byte)
func (c *RedisCache) MaxEntrySize() int64 {
	return c.maxEntrySize
}

// StorageType 获取缓存存储驱动类型
func (c *RedisCache) StorageType() storage.StorageType {
	return storage.StorageRedis
}

// DefaultExpire 获取默认过期时间(精度为秒)
func (c *RedisCache) DefaultExpire() time.Duration {
	return c.defaultExpire
}

// UseLocal 仅使用 Local 缓存进行操作( 通常为 MemoryCache )
//   - 仅在 L2Cache 驱动模式下有效, 其他驱动模式返回自身驱动实例
func (c *RedisCache) UseLocal() storage.Storager {
	return c
}

// UseRemote 仅仅使用 Remote 进行缓存操作( 通常为 RedisCache )
//   - 仅在 L2Cache 驱动模式下有效, 其他驱动模式返回自身驱动实例
func (c *RedisCache) UseRemote() storage.Storager {
	return c
}

// LiveState 获取当前缓存实例的运行状态
func (c *RedisCache) LiveState() storage.LiveState {
	if c.isClosed.Load() {
		return storage.LiveStateShutdown
	}
	// 若内存占用接近最大容量的99%, 则视为异常状态
	if c.MaxSize() > 0 && c.TotalSize() > (c.MaxSize()*99/100) {
		return storage.LiveStateFailure
	}
	return storage.LiveStateNormal
}

// IsGlobal 是否为全局缓存实例
func (c *RedisCache) IsGlobal() bool {
	return c.isGlobal
}

// SetGlobal 设置实例为全局缓存标记
func (c *RedisCache) SetGlobal() {
	c.isGlobal = true
}
